#!/usr/bin/env python
# encoding: utf-8
'''
@author: Excelsiorly
@license: (C) Copyright 2022, All Rights Reserved.
@contact: excelsiorly@qq.com
@file: 146. LRU缓存机制.py
@time: 2022/1/5 13:25
@desc: https://leetcode-cn.com/problems/lru-cache/
请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类：
● LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
● int get(int key) 如果关键字 key 存在于缓存中，则返回关键字的值，否则返回 -1 。
● void put(int key, int value) 如果关键字 key 已经存在，则变更其数据值 value ；如果不存在，则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ，则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

@难点：
    1. put()要求O(1)，那么就要求不能每次找最久未使用的值都排个序（因为至少要O(log2n）
    2. 使用双向链表就要实现大量相关操作
@解题思路：
    使用双向链表+哈希表：
        1. 哈希表用来记录值，这里的值不直接使用整数，而使用链表结点
        2. 每次get一个值都将对应结点插入到最前面。就能保证双向链表有起到保存访问记录的作用
        3. 每次put涉及到弹出时都从双向链表队尾中删掉结点
'''


class LinkedNode:
    """
    双向链表
    """
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None


class LRUCache(object):

    def __init__(self, capacity):
        """
        :type capacity: int
        """
        # 内存
        self.cache = {}
        # 头部、尾部
        self.head = LinkedNode()
        self.tail = LinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity
        self.size = 0

    def get(self, key):
        """
        :type key: int
        :rtype: int
        """
        if not key in self.cache:
            return -1
        # 如果key存在，则返回，并将该结点插入到头节点
        node = self.cache[key]
        self.moveToHead(node)
        return node.value

    def put(self, key, value):
        """
        :type key: int
        :type value: int
        :rtype: None
        """
        if key not in self.cache:
            # key不存在就创建一个新的结点并插入
            node = LinkedNode(key, value)
            self.cache[key] = node
            # 加入到头部
            self.addToHead(node)
            self.size += 1
            # 如果超容量了就删掉最后的结点
            if self.size > self.capacity:
                deleted = self.removeTail()
                self.cache.pop(deleted.key)
                self.size -= 1
        else:
            # 存在就直接修改值并插到队头
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)

    def removeNode(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def addToHead(self, node):
        node.next = self.head.next
        node.prev = self.head
        self.head.next.prev = node
        self.head.next = node

    def moveToHead(self, node):
        self.removeNode(node)
        self.addToHead(node)

    def removeTail(self):
        tail = self.tail.prev
        self.removeNode(tail)
        return tail

# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)